package com.dnr.arch.kernel.model;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.persistence.Entity;

import com.dnr.arch.kernel.KernelUtility;
import com.dnr.arch.kernel.api.model.FieldInterface;
import com.dnr.arch.kernel.api.model.MetaClassInterface;
import com.dnr.arch.kernel.api.model.Value;
import com.dnr.arch.kernel.api.model.relations.Aggregation;
import com.dnr.arch.kernel.api.model.relations.Cardinality;
import com.dnr.arch.kernel.api.model.relations.Relation;
import com.dnr.arch.kernel.services.persistence.relations.AggregationJPAImpl;
import com.dnr.arch.kernel.services.persistence.relations.CardinalityJPAImpl;
import com.dnr.arch.kernel.services.persistence.util.PersistenceUtility;

public class MetaClass implements MetaClassInterface {

	private Collection<FieldInterface> fields;
	private AbstractValue cls;
	private String className;
	private Collection<Relation> relations;

	private boolean isGenerateInvoked = false;

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.dnr.arch.kernel.model.MetaClassInterface#getFields()
	 */
	@Override
	public Collection<FieldInterface> getFields() {
		if (!isGenerateInvoked) {
			generate();
			isGenerateInvoked = true;
		}
		return fields;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.dnr.arch.kernel.model.MetaClassInterface#setFields(java.util.Collection
	 * )
	 */

	public void setFields(Collection<FieldInterface> fields) {
		this.fields = fields;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.dnr.arch.kernel.model.MetaClassInterface#getClassName()
	 */
	@Override
	public String getClassName() {
		return className;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.dnr.arch.kernel.model.MetaClassInterface#setClassName(java.lang.String
	 * )
	 */

	public void setClassName(String className) {
		this.className = className;
	}

	public MetaClass(String className) {
		this.className = className;
		fields = new ArrayList<FieldInterface>();
	}

	public MetaClass(AbstractValue cls) {
		this.cls = cls;
		this.className = cls.getClass().getName();
		fields = new ArrayList<FieldInterface>();
	}

	private void generate() {
		try {
			if (cls == null) {
				Class<?> c = getClass().getClassLoader().loadClass(className);
				cls = (AbstractValue) c.newInstance();
			}

			// Test if JPA Entity annotation is present
			if (cls.getClass().getAnnotation(javax.persistence.Entity.class) == null) {
				return;
			}

			for (Field field : cls.getClass().getDeclaredFields()) {
				if (PersistenceUtility.isFieldAnnotated(field)) {
					String s = KernelUtility.getGetterMethod(field.getName());
					FieldInterface v = null;
					try {
						Method m = cls.getClass().getMethod(s, null);
						v = new ValueField(field, m.invoke(cls, null));
					} catch (NoSuchMethodException e) {
						Method m = cls.getClass().getMethod(s + "s", null);
						v = new ValueField(field, m.invoke(cls, null));
					}
					fields.add(v);
				}
			}
			// for(Annotation annot: c.getAnnotations()){
			// String s = annot.annotationType().toString();
			// s.length();
			// }

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public Collection<Relation> getRelations() {
		relations = new ArrayList<Relation>();
		if (fields != null && fields.size() > 0) {
			for (Iterator<FieldInterface> it = fields.iterator(); it.hasNext();) {
				// Class c = it.next().getFieldType();
				// Object o = c.getSuperclass();
				// String s = "ss";
				FieldInterface f = it.next();
				try {
					if (PersistenceUtility.isRelationAnnotated(cls.getClass()
							.getDeclaredField(f.getFieldName()))) {
						MetaClassInterface c2 = new MetaClass(f.getFieldType()
								.getName());
						
						Object o = f.getFieldType().getTypeParameters();

						Aggregation agg = new AggregationJPAImpl(this, c2);

						Cardinality card1 = null;
						Cardinality card2 = null;
						if (f.getFieldType().getAnnotation(
								javax.persistence.ManyToOne.class) != null) {
							card1 = new CardinalityJPAImpl(agg, this, "0",
									PersistenceUtility.MAX_CARDINALITY);
							card2 = new CardinalityJPAImpl(agg, c2, "0", "1");
						} else {
							card1 = new CardinalityJPAImpl(agg, this, "0", "1");
							card2 = new CardinalityJPAImpl(agg, c2, "0",
									PersistenceUtility.MAX_CARDINALITY);
						}

						agg.setCardinalities(card1, card2);
						relations.add(agg);
					}
				} catch (SecurityException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (NoSuchFieldException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

		}
		return relations;
	}

	@Override
	public Collection<Class<?>> getInterfaces() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Class<?> getSuperClass() {
		// TODO Auto-generated method stub
		return null;
	}
}
